package exclude

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/util/convert"
	uTime "go.manyji.com/lib/util/time"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"runtime/debug"
	"sort"
	"strconv"
	"strings"
	"time"
)

var (
	LogErr = log.Err
	Log    = log.Info
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func Random(min int, max int) int {
	if min < 0 {
		min = -min
	}
	if max < 0 {
		max = -max
	}
	if min >= max {
		return min
	}
	return rand.Intn(max-min+1) + min
}

func getValidListener(ip string, from, to int) (net.Listener, int, error) {
	ports := []int{0}
	if to > from {
		ports = rand.Perm(to - from + 1)
	}
	for _, p := range ports {
		p += from
		l, e := net.Listen("tcp", ip+":"+strconv.Itoa(p))
		if e == nil {
			return l, p, nil
		}
	}
	return nil, 0, errors.New("No valid port")
}

func getLocalIP() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}
	for _, addr := range addrs {
		s := addr.String()
		if !strings.HasPrefix(s, "10.") && !strings.HasPrefix(s, "192.168") {
			continue
		}
		ss := strings.Split(s, "/")
		return ss[0], nil
	}
	return "", errors.New("No valid IP")
}

func getObjAddr(obj interface{}) string {
	s := fmt.Sprintf("%p", obj)
	i, _ := strconv.ParseInt(s, 0, 64)
	return strconv.FormatInt(i, 10)
}

func GetMD5(s string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(s)))
}

func httpGet(u string) (string, error) {
	defer recoverPanic()
	u, err := url.QueryUnescape(u)
	if err != nil {
		return "", err
	}
	u = strings.Replace(u, "+", "%20", -1)
	res, err := http.Get(u)
	if err != nil {
		return "", err
	}
	content, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return "", err
	}
	return string(content), nil
}

func httpPost(u string, data map[string]string) (string, error) {
	defer recoverPanic()
	u, err := url.QueryUnescape(u)
	if err != nil {
		return "", err
	}
	values := make(url.Values)
	for k, v := range data {
		values.Add(k, v)
	}
	res, err := http.PostForm(u, values)
	if err != nil {
		return "", err
	}
	content, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return "", err
	}
	if string(content) != "0" {
		LogErr("httpPost:", string(content))
	}
	return string(content), nil
}

func createSig(secret string, data map[string]string) string {
	keys := []string{}
	for key := range data {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	s := ""
	for _, key := range keys {
		s += key + "=" + data[key]
	}
	s += secret

	return GetMD5(s)
}

type ReqArgs map[string]string

func parsePostForm(kvs url.Values) ReqArgs {
	r := ReqArgs{}
	for k := range kvs {
		r[k] = kvs.Get(k)
	}
	return r
}

func parseReqUrl(u string) (path string, origReqStr string, args ReqArgs, err error) {
	ss := strings.Split(u, "?")
	if len(ss) > 2 {
		err = errors.New("format error")
		return
	}
	path = ss[0]
	if len(ss) == 1 || ss[1] == "" {
		return
	}
	origReqStr = ss[1]
	args = ReqArgs{}
	qs := strings.Split(ss[1], "&")
	for _, q := range qs {
		kv := strings.Split(q, "=")
		if len(kv) != 2 {
			continue
		}
		if v, err := url.QueryUnescape(kv[1]); err == nil {
			args[kv[0]] = v
		}
	}
	return
}

func (r ReqArgs) Get(key string) string {
	return r[key]
}

func (r ReqArgs) Del(key string) {
	delete(r, key)
}

func (r ReqArgs) Add(key, value string) {
	r[key] = value
}

func (r ReqArgs) String() string {
	j, _ := JsonEncode(r)
	return j
}

//

func toInt(v string) int {
	n, err := strconv.Atoi(v)
	if err != nil {
		n = 0
	}
	return n
}

func toInt64(v string) int64 {
	n, err := strconv.ParseInt(v, 10, 64)
	if err != nil {
		n = 0
	}
	return n
}

func toIntStr(v int) string {
	return strconv.Itoa(v)
}

func toInt64Str(v int64) string {
	return strconv.FormatInt(v, 10)
}

func JsonEncode(v interface{}) (string, error) {
	buf := new(bytes.Buffer)
	enc := json.NewEncoder(buf)
	enc.SetEscapeHTML(false)
	err := enc.Encode(v)
	return strings.TrimSpace(buf.String()), err
}

func JsonDecode(data string, v interface{}) error {
	return json.Unmarshal([]byte(data), v)
}

func substr(s string, l int) string {
	if len(s) <= l {
		return s
	}
	ss, sl, rs := "", 0, []rune(s)
	for _, r := range rs {
		if sl+1 > l {
			break
		}
		sl += 1
		ss += string(r)
	}
	return ss
}

func show_strlen(s string) int {
	sl := 0
	rs := []rune(s)
	for _, r := range rs {
		rint := int(r)
		if rint < 128 {
			sl++
		} else {
			sl += 2
		}
	}
	return sl
}

func getTodayLeftTime() int {
	t := time.Now()
	tm1 := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
	tm2 := tm1.AddDate(0, 0, 1)
	return int(tm2.Sub(t).Seconds())
}

func getTimeByYmd(Ymd string) time.Time {
	arr := []byte(Ymd)
	Y := arr[0:4]
	m := arr[4:6]
	d := arr[6:8]
	return time.Date(toInt(string(Y)), time.Month(toInt(string(m))), toInt(string(d)), 0, 0, 0, 0, time.Now().Location())
}

func getNextDayTime() int64 {
	t := time.Now()
	tm1 := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
	tm2 := tm1.AddDate(0, 0, 1)
	return tm2.Unix()
}

var sqlFilter = newSqlFilter("%", "\"", "'", ";", "`", "-")

func newSqlFilter(keys ...string) *strings.Replacer {
	oldnews := []string{}
	for _, k := range keys {
		oldnews = append(oldnews, k, "")
	}
	return strings.NewReplacer(oldnews...)
}

func defaultString(val, defaultVal string) string {
	if val == "" {
		return defaultVal
	}
	return val
}

func minInt(a int, b int) int {
	if a > b {
		return b
	}
	return a
}

func minInt64(a int64, b int64) int64 {
	if a > b {
		return b
	}
	return a
}

func maxInt(a int, b int) int {
	if a > b {
		return a
	}
	return b
}

func maxInt64(a int64, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func pad(s string, l int) string {
	if len(s) == l {
		return s
	}
	if len(s) > l {
		return s[:l]
	}
	return strings.Repeat("0", l-len(s)) + s
}

func FmtDayDate(day int) string {
	_day := convert.ToString(day)
	if len(_day) != 8 {
		return _day
	}
	return _day[:4] + "-" + _day[4:6] + "-" + _day[6:8]
}

func getTodayDiffDays(day int) int {
	_day := convert.ToString(day)
	if len(_day) != 8 {
		return 0
	}
	t := time.Now().Unix()
	t2, _ := uTime.ParseTimestamp(_day+"235959", "YmdHis")
	return int(t2-t) / 86400
}
func getExcelDays(day int) string {
	t := time.Date(1900, 1, 0, 0, 0, 0, 0, time.Local)
	days := day - 1
	t2 := t.AddDate(0, 0, days)
	return uTime.FmtDate(t2, "Ymd")
}
func ChangeSql(fileds string) string {
	fieldArr := strings.Split(fileds, ",")
	sql := strings.Repeat(",?", len(fieldArr))
	sql1 := sql[1:len(sql)]
	return convert.ToString(sql1)
}
func ChangeSqlArr(fileds []string) string {
	sql := strings.Repeat(",?", len(fileds))
	sql1 := sql[1:len(sql)]
	return convert.ToString(sql1)
}
func stringToIntArray(data []string) []int {
	info := []int{}
	for _, v := range data {
		info = append(info, convert.Atoi(v))
	}
	return info
}
func MD5(s string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(s)))
}

func recoverPanic() {
	if err := recover(); err != nil {
		Log("PANIC: %s", err)
		Log(string(debug.Stack()))
	}
}
